//
// Created by Key-Box-Min on 11/07/2020.
//

#include "NetBase.h"

using namespace NetBase;

// linux ############################################################################################

#if __linux
//Base

int Base::net_bind (int __fd, const sockaddr* __addr, int __len){
    return bind(__fd,__addr,__len);}
int Base::net_accept(int __fd, sockaddr* __addr, uint32_t * __addr_len){
    return accept(__fd,__addr,__addr_len);}
int Base::net_connect(int __fd, const sockaddr* __addr, int __len){
    return connect(__fd,__addr,__len);}
int Base::net_send (int __fd, const int8_t *__buf, uint32_t __n, int __flags){
    return send(__fd,__buf,__n,__flags);}
int Base::net_recv (int __fd, int8_t *__buf, uint32_t __n, int __flags){
    return recv(__fd,__buf,__n,__flags);}
int Base::net_sendto (int __fd, const int8_t *__buf, uint32_t __n,int __flags, const sockaddr* __addr, uint32_t __addr_len){
    return sendto(__fd,__buf,__n,__flags,__addr,__addr_len);}
int Base::net_recvfrom (int __fd, int8_t *__restrict __buf, uint32_t __n,int __flags, sockaddr* __addr,uint32_t * __addr_len){
    return recvfrom(__fd,__buf,__n,__flags,__addr,__addr_len);}
int Base::net_socket(int __af,int __type,int __protocol){
    static InitNetEnvironmental init;
    return socket(__af,__type,__protocol);}
int Base::net_close(int sock){
    return close(sock);}

Base::InitNetEnvironmental::InitNetEnvironmental(){}
Base::InitNetEnvironmental::~InitNetEnvironmental(){}

//NetAddress

NetAddress::NetAddress(std::string addr,int port){
    sockaddr_in* addr_in = (sockaddr_in*)&__addr;
    addr_in->sin_family = AF_INET;
    addr_in->sin_addr.s_addr = inet_addr(addr.data());
    addr_in->sin_port = htons(port);
}

NetAddress::NetAddress(uint32_t addr,int port){
    sockaddr_in* addr_in = (sockaddr_in*)&__addr;
    addr_in->sin_family = AF_INET;
    addr_in->sin_addr.s_addr = addr;
    addr_in->sin_port = htons(port);
}

// Tcp / Udp ====================================================================

//NetTcp

NetTcp::NetTcp():NetBaseAbstract(){
    __sock = Base::net_socket(AF_INET,SOCK_STREAM,0);
}
NetTcp::NetTcp(int sock):NetBaseAbstract(sock){}

bool NetTcp::bind(const NetAddress& addr,int count){
    __addr = addr.getAddr();
    __count = count;

    if(Base::net_bind(__sock,&__addr.getAddr(),sizeof(__addr))<0)return false;
    if(listen(__sock,__count)<0)return false;
    return true;
}
NetTcp* NetTcp::accept(){
    sockaddr _addr = {};
    int cli = accept(_addr);
    if(cli < 0)return nullptr;

    NetTcp* client = new NetTcp;
    client->__sock = cli;
    client->__addr = _addr;
    return client;
}
int NetTcp::accept(sockaddr& addr) {
    uint32_t len = sizeof(addr);
    return Base::net_accept(__sock,&addr,&len);
}
bool NetTcp::connect(const NetAddress& addr){
    __addr = addr.getAddr();
    if(Base::net_connect(__sock,&__addr.getAddr(),sizeof (__addr)) < 0)return false;
    return true;
}

int NetTcp::send(const int8_t* data,const uint32_t& len,int flags){
    return Base::net_send(__sock,data,len,flags);
}
int NetTcp::recv(int8_t* data,const uint32_t& len,int flags){
    return Base::net_recv(__sock,data,len,flags);
}

//NetUdp

NetUdp::NetUdp():NetBaseAbstract(){
    __sock = Base::net_socket(AF_INET,SOCK_DGRAM,0);
}
NetUdp::NetUdp(int sock):NetBaseAbstract(sock){}

bool NetUdp::bind(const NetAddress& addr){
    __addr = addr;
    if(Base::net_bind(__sock,&__addr.getAddr(),sizeof(__addr))<0)return false;
    return true;
}
int NetUdp::send(const NetAddress& addr,const int8_t* data,const uint32_t& len,int flags){
    return Base::net_sendto(__sock,data,len,flags,&addr.getAddr(),sizeof(__addr));
}
int NetUdp::recv(NetAddress& addr,int8_t* data,const uint32_t& len,int flags){
    static uint32_t socklen = sizeof(struct sockaddr);
    return Base::net_recvfrom(__sock,data,len,flags,&addr.getAddr(),&socklen);
}

#endif

// win ############################################################################################

#if WIN32
//Base
int Base::net_bind (int __fd, const sockaddr* __addr, int __len){
    return bind(__fd,__addr,__len);}
int Base::net_accept(int __fd, sockaddr* __addr,uint32_t * __addr_len){
    return accept(__fd,__addr,(int*)__addr_len);}
int Base::net_connect(int __fd, const sockaddr* __addr, int __len){
    return connect(__fd,__addr,__len);}
int Base::net_send (int __fd, const int8_t *__buf, uint32_t __n, int __flags){
    return send(__fd,(const char*)__buf,__n,__flags);}
int Base::net_recv (int __fd, int8_t *__buf, uint32_t __n, int __flags){
    return recv(__fd,(char*)__buf,__n,__flags);}
int Base::net_sendto (int __fd, const int8_t*__buf, uint32_t __n,int __flags, const sockaddr* __addr, uint32_t __addr_len){
    return sendto(__fd,(char*)__buf,__n,__flags,__addr,__addr_len);}
int Base::net_recvfrom (int __fd,int8_t* __buf, uint32_t __n,int __flags, sockaddr* __addr,uint32_t * __addr_len){
    return recvfrom(__fd,(char*)__buf,__n,__flags,__addr,(int*)__addr_len);}
int Base::net_socket(int __af,int __type,int __protocol){
    static InitNetEnvironmental init;
    return socket(__af,__type,__protocol);}
int Base::net_close(int sock){
    return closesocket(sock);}

Base::InitNetEnvironmental::InitNetEnvironmental(){
    WORD ver = MAKEWORD(2,2);
    WSADATA dat;
    WSAStartup(ver,&dat);
}
Base::InitNetEnvironmental::~InitNetEnvironmental(){
    WSACleanup();
}

//NetAddress

NetAddress::NetAddress(std::string addr,int port){
    sockaddr_in* addr_in = (sockaddr_in*)&__addr;
    addr_in->sin_family = AF_INET;
    addr_in->sin_addr.S_un.S_addr = inet_addr(addr.data());
    addr_in->sin_port = htons(port);
}

NetAddress::NetAddress(uint32_t addr,int port){
    sockaddr_in* addr_in = (sockaddr_in*)&__addr;
    addr_in->sin_family = AF_INET;
    addr_in->sin_addr.S_un.S_addr = addr;
    addr_in->sin_port = htons(port);
}

// Tcp / Udp ====================================================================

//NetTcp

NetTcp::NetTcp():NetBaseAbstract(){
    __sock = Base::net_socket(AF_INET,SOCK_STREAM,0);
}
NetTcp::NetTcp(int sock):NetBaseAbstract(sock){}

bool NetTcp::bind(const NetAddress& addr,int count){
    __addr = addr.getAddr();
    __count = count;

    if(Base::net_bind(__sock,&__addr.getAddr(),sizeof(__addr))<0)return false;
    if(listen(__sock,__count)<0)return false;
    return true;
}
NetTcp* NetTcp::accept(){
    sockaddr addr = {};
    int cli = accept(addr);
    if(cli < 0)return nullptr;

    NetTcp* client = new NetTcp;
    client->__sock = cli;
    client->__addr = addr;
    return client;
}
int NetTcp::accept(sockaddr& addr) {
    uint32_t len = sizeof(addr);
    return Base::net_accept(__sock,&addr,&len);
}
bool NetTcp::connect(const NetAddress& addr){
    __addr = addr.getAddr();
    if(Base::net_connect(__sock,&__addr.getAddr(),sizeof (__addr)) < 0)return false;
    return true;
}

int NetTcp::send(const int8_t* data,const uint32_t& len,int flags){
    return Base::net_send(__sock,data,len,flags);
}
int NetTcp::recv(int8_t* data,const uint32_t& len,int flags){
    return Base::net_recv(__sock,data,len,flags);
}

//NetUdp

NetUdp::NetUdp():NetBaseAbstract(){
    __sock = Base::net_socket(AF_INET,SOCK_DGRAM,0);
}
NetUdp::NetUdp(int sock):NetBaseAbstract(sock){}

bool NetUdp::bind(const NetAddress& addr){
    __addr = addr;
    if(Base::net_bind(__sock,&__addr.getAddr(),sizeof(__addr))<0)return false;
    return true;
}
int NetUdp::send(const NetAddress& addr,const int8_t* data,const uint32_t& len,int flags){
    return Base::net_sendto(__sock,data,len,flags,&addr.getAddr(),sizeof(__addr));
}
int NetUdp::recv(NetAddress& addr,int8_t* data,const uint32_t& len,int flags){
    static uint32_t socklen = sizeof(struct sockaddr);
    return Base::net_recvfrom(__sock,data,len,flags,&addr.getAddr(),&socklen);
}

#endif

//Test====================================================================

void Test::netBase_Tcp_Client(NetAddress addr, std::string msg, int count) {
    int8_t* buf = new int8_t[msg.size()+1];
    memcpy(buf, msg.data(), msg.size());

    NetTcp client;
    if(!client.connect(addr)){
        std::cout<<"connect error"<<std::endl;
        client.close();
        return;
    }

    int i=0;
    while (count == -1 || i++ < count){
        memcpy(buf, msg.data(), msg.size());
        if(client.send((int8_t*)buf,sizeof(buf)) <= 0){
            std::cout<<"send error"<<std::endl;
            client.close();
            return;
        }

        std::cout<<"send ok"<<std::endl;
        memset(buf,'\0',sizeof(buf));

        if (client.recv(buf,sizeof(buf)-1) <= 0){
            std::cout<<"recv error"<<std::endl;
            client.close();
            return;
        }
        std::cout<<"recv: "<<buf<<std::endl;
    }
    client.close();
};

void Test::netBase_Udp_Client(NetAddress addr, std::string msg, int count) {
    int8_t* buf = new int8_t[msg.size()+1];

    NetUdp client;

    int i=0;
    while (count == -1 || i++ < count) {
        memcpy(buf, msg.data(), msg.size());
        if (client.send(addr, (int8_t *) buf, sizeof(buf)) <= 0) {
            std::cout << "send error" << std::endl;
            client.close();
            return;
        }

        std::cout << "send ok" << std::endl;
        memset(buf,'\0',sizeof(buf));

        if (client.recv(addr, buf, sizeof(buf) - 1) <= 0) {
            std::cout << "recv error" << std::endl;
            client.close();
            return;
        }
        std::cout << "recv: " << buf << std::endl;
    }
    client.close();
}